home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue40 / Diagram / JimShape.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-10-27  |  22.9 KB  |  848 lines

  1. unit JimShape;
  2.  
  3. {$B-}
  4.  
  5. interface
  6.  
  7. uses
  8.   Windows, Controls, Classes;
  9.  
  10. type
  11.   TjimCustomShape = class(TGraphicControl)
  12.     // All controls descend from this, to help with streaming and unique naming
  13.   private
  14.   protected
  15.   public
  16.     constructor Create(AOwner : TComponent); override;
  17.  
  18.     procedure SetBounds(ALeft,ATop,AWidth,AHeight : Integer); override;
  19.   published
  20.     // Make these properties available, so we can respond to mouse clicks
  21.     property OnClick;
  22.     property OnDblClick;
  23.   end;
  24.  
  25.  
  26.   TjimTextShape = class(TjimCustomShape)
  27.   private
  28.     FText     : string;
  29.     FAutosize : Boolean;
  30.  
  31.     procedure SetText(Value : string);
  32.     procedure SetAutosize(Value : Boolean);
  33.   protected
  34.     procedure Paint; override;
  35.   public
  36.     constructor Create(AOwner : TComponent); override;
  37.  
  38.     procedure SetBounds(ALeft,ATop,AWidth,AHeight : Integer); override;
  39.   published
  40.     property Text     : string read FText write SetText;
  41.     property Autosize : Boolean read FAutosize write SetAutosize;
  42.   end;
  43.  
  44.  
  45.   TjimBitmapShape = class(TjimCustomShape)
  46.   private
  47.     FImages     : TImageList;
  48.     FImageIndex : Integer;
  49.     FCaption    : TjimTextShape;
  50.  
  51.     procedure SetImages(Value : TImageList);
  52.     procedure SetImageIndex(Value : Integer);
  53.     procedure SetCaption(Value : TjimTextShape);
  54.   protected
  55.     procedure Paint; override;
  56.     procedure Notification(AComponent : TComponent;Operation : TOperation); override;
  57.   public
  58.     constructor Create(AOwner : TComponent); override;
  59.  
  60.     procedure SetBounds(ALeft,ATop,AWidth,AHeight : Integer); override;
  61.   published
  62.     property Images     : TImageList read FImages write SetImages;
  63.     property ImageIndex : Integer read FImageIndex write SetImageIndex;
  64.     property Caption    : TjimTextShape read FCaption write SetCaption;
  65.   end;
  66.  
  67.  
  68.   TjimConnectionSide = (csLeft,csRight,csTop,csBottom);
  69.  
  70.  
  71.   TjimConnection = class(TPersistent)
  72.   private
  73.     FShape  : TjimCustomShape;
  74.     FSide   : TjimConnectionSide;  // Side to connect to
  75.     FOffset : Integer;             // Distance from top or left of side
  76.   public
  77.     constructor Create;
  78.  
  79.     procedure Assign(Source : TPersistent); override;
  80.     // Gets connection point in parent's coordinates
  81.     function ConnPoint(TerminatorRect : TRect): TPoint;
  82.     // Gets terminator connection point in parent's coordinates
  83.     function TermPoint(TerminatorRect : TRect): TPoint;
  84.     // Functions to get boundaries of the terminators
  85.     function LeftMost(TerminatorRect : TRect): TPoint;
  86.     function RightMost(TerminatorRect : TRect): TPoint;
  87.     function TopMost(TerminatorRect : TRect): TPoint;
  88.     function BottomMost(TerminatorRect : TRect): TPoint;
  89.   published
  90.     property Shape  : TjimCustomShape read FShape write FShape;
  91.     property Side   : TjimConnectionSide read FSide write FSide;
  92.     property Offset : Integer read FOffset write FOffset;
  93.   end;
  94.  
  95.  
  96.   TjimConnector = class(TjimCustomShape)
  97.   private
  98.     FLineWidth  : Integer;
  99.     // The shapes connected by this control
  100.     FStartConn : TjimConnection;
  101.     FEndConn   : TjimConnection;
  102.     // Area of the terminator symbol to be drawn (in horizontal position)
  103.     FStartTermRect : TRect;
  104.     FEndTermRect   : TRect;
  105.  
  106.     procedure SetLineWidth(Value : Integer);
  107.     function  GetConn(Index : Integer) : TjimConnection;
  108.     procedure SetConn(Index : Integer;Value : TjimConnection);
  109.     function  GetTermRect(Index : Integer) : TRect;
  110.     procedure SetTermRect(Index : Integer;Value : TRect);
  111.   protected
  112.     procedure Paint; override;
  113.     procedure Notification(AComponent : TComponent;Operation : TOperation); override;
  114.     // For drawing arrows etc. Called from Paint.
  115.     procedure DrawStartTerminator; virtual;
  116.     procedure DrawEndTerminator; virtual;
  117.     // Converts point from parent's coordinates to own coordinates
  118.     function  Convert(APoint : TPoint) : TPoint;
  119.     function  IsConnected(ConnectedShape : TjimCustomShape) : Boolean;
  120.   public
  121.     constructor Create(AOwner : TComponent); override;
  122.     destructor  Destroy; override;
  123.  
  124.     // Restrict the minimum size
  125.     procedure SetBounds(ALeft,ATop,AWidth,AHeight : Integer); override;
  126.     // Called when moving one of the connected shapes
  127.     procedure SetBoundingRect;
  128.     procedure SetConnections(TheStartConn,TheEndConn : TjimConnection);
  129.  
  130.     property StartTermRect : TRect index 1 read GetTermRect write SetTermRect;
  131.     property EndTermRect   : TRect index 2 read GetTermRect write SetTermRect;
  132.   published
  133.     // Publish these properties so that component streaming can be used to
  134.     // store them in a file
  135.     property LineWidth : Integer read FLineWidth write SetLineWidth default 1;
  136.     property StartConn : TjimConnection index 1 read GetConn write SetConn;
  137.     property EndConn   : TjimConnection index 2 read GetConn write SetConn;
  138.   end;
  139.  
  140.  
  141.   TjimSingleHeadArrow = class(TjimConnector)
  142.   protected
  143.     procedure DrawArrowHead(ConnPt,TermPt : TPoint);
  144.     procedure DrawEndTerminator; override;
  145.   public
  146.     constructor Create(AOwner : TComponent); override;
  147.   end;
  148.  
  149.  
  150. implementation
  151.  
  152. uses
  153.   SysUtils, Graphics, ImgList;
  154.  
  155.  
  156. var
  157.   FShapeCount : Integer;
  158.   // Used in unique naming scheme. It is global in this unit to enable a
  159.   // 'memory' of the component names used during the lifetime of this unit.
  160.  
  161.  
  162. procedure NoLessThan(var Value : Integer;Limit : Integer);
  163. begin {NoLessThan}
  164.   if Value < Limit then begin
  165.     Value := Limit;
  166.   end;
  167. end;  {NoLessThan}
  168.  
  169.  
  170. function RectHeight(ARect : TRect) : Integer;
  171. begin {RectHeight}
  172.   Result := ARect.Bottom - ARect.Top;
  173. end;  {RectHeight}
  174.  
  175.  
  176. function RectWidth(ARect : TRect) : Integer;
  177. begin {RectWidth}
  178.   Result := ARect.Right - ARect.Left;
  179. end;  {RectWidth}
  180.  
  181.  
  182. function Min(A : array of Integer) : Integer;
  183.   var
  184.     i : Integer;
  185. begin {Min}
  186.   Result := 0;  // Purely to stop compiler warnings
  187.  
  188.   for i := Low(A) to High(A) do begin
  189.     if i = Low(A) then begin
  190.       Result := A[i]
  191.     end else if A[i] < Result then begin
  192.       Result := A[i];
  193.     end;
  194.   end;
  195. end;  {Min}
  196.  
  197.  
  198. function Max(A : array of Integer) : Integer;
  199.   var
  200.     i : Integer;
  201. begin {Max}
  202.   Result := 0;  // Purely to stop compiler warnings
  203.  
  204.   for i := Low(A) to High(A) do begin
  205.     if i = Low(A) then begin
  206.       Result := A[i]
  207.     end else if A[i] > Result then begin
  208.       Result := A[i];
  209.     end;
  210.   end;
  211. end;  {Max}
  212.  
  213.  
  214. // ---------------------------- TjimCustomShape ------------------------------
  215.  
  216. constructor TjimCustomShape.Create(AOwner : TComponent);
  217.   var
  218.     AlreadyUsed : Boolean;
  219.     i           : Integer;
  220.     TempName    : string;
  221. begin {Create}
  222.   inherited Create(AOwner);
  223.  
  224.   // Give the component a name and ensure that it is unique
  225.   repeat
  226.     // Use a local variable to hold the name, so that don't get exceptions
  227.     // raised on duplicate names
  228.     TempName := 'Shape' + IntToStr(FShapeCount);
  229.     Inc(FShapeCount);
  230.     AlreadyUsed := False;
  231.  
  232.     // Loop through all the components on the form to ensure that this name
  233.     // is not already in use
  234.     for i := 0 to Owner.ComponentCount - 1 do begin
  235.       if Owner.Components[i].Name = TempName then begin
  236.         // Try the next component name as this one is used already
  237.         AlreadyUsed := True;
  238.         Break;
  239.       end;
  240.     end;
  241.   until not AlreadyUsed;
  242.  
  243.   Name := TempName;
  244. end;  {Create}
  245.  
  246.  
  247. procedure TjimCustomShape.SetBounds(ALeft,ATop,AWidth,AHeight : Integer);
  248.   var
  249.     i : Integer;
  250. begin {SetBounds}
  251.   inherited SetBounds(ALeft,ATop,AWidth,AHeight);
  252.  
  253.   // Search for any connectors between this and any other control
  254.   // First check that this control has been placed on a form
  255.   if not Assigned(Parent) then begin
  256.     Exit;
  257.   end;
  258.  
  259.   // Search parent control for TjimConnector components
  260.   for i := 0 to Parent.ControlCount - 1 do begin
  261.     if Parent.Controls[i] is TjimConnector then begin
  262.       with TjimConnector(Parent.Controls[i]) do begin
  263.         // Check if this component is at either end of the connector
  264.         if IsConnected(Self) then begin
  265.           // Resize the connector
  266.           TjimConnector(Parent.Controls[i]).SetBoundingRect;
  267.         end;
  268.       end;
  269.     end;
  270.   end;
  271. end;  {SetBounds}
  272.  
  273.  
  274. // ----------------------------- TjimTextShape  ------------------------------
  275.  
  276. constructor TjimTextShape.Create(AOwner : TComponent);
  277. begin {Create}
  278.   inherited Create(AOwner);
  279.   FAutosize := True;
  280.   FText     := '';
  281. end;  {Create}
  282.  
  283.  
  284. procedure TjimTextShape.SetText(Value : string);
  285. begin {SetText}
  286.   if FText <> Value then begin
  287.     FText := Value;
  288.     SetBounds(Left,Top,Width,Height);
  289.   end;
  290. end;  {SetText}
  291.  
  292.  
  293. procedure TjimTextShape.SetAutosize(Value : Boolean);
  294. begin {SetText}
  295.   if FAutosize <> Value then begin
  296.     FAutosize := Value;
  297.     SetBounds(Left,Top,Width,Height);
  298.   end;
  299. end;  {SetText}
  300.  
  301.  
  302. procedure TjimTextShape.Paint;
  303.   var
  304.     TempRect : TRect;
  305. begin {Paint}
  306.   inherited Paint;
  307.  
  308.   if not Assigned(Parent) then begin
  309.     Exit;
  310.   end;
  311.  
  312.   TempRect := ClientRect;  // So can pass as a var parameter
  313.   DrawText(Canvas.Handle,PChar(FText),Length(FText),TempRect,
  314.            DT_CENTER or DT_NOPREFIX or DT_WORDBREAK);
  315. end;  {Paint}
  316.  
  317.  
  318. procedure TjimTextShape.SetBounds(ALeft,ATop,AWidth,AHeight : Integer);
  319. begin {SetBounds}
  320.   // Check that the control bounds are sensible. Note that this also works
  321.   // if try to set Left, Top etc properties, as their access methods call
  322.   // SetBounds().
  323.   if FAutosize and Assigned(Parent) then begin
  324.     NoLessThan(AWidth,Canvas.TextWidth(FText));
  325.     NoLessThan(AHeight,Canvas.TextHeight(FText));
  326.   end;
  327.  
  328.   inherited SetBounds(ALeft,ATop,AWidth,AHeight);
  329. end;  {SetBounds}
  330.  
  331.  
  332. // ---------------------------- TjimBitmapShape ------------------------------
  333.  
  334. constructor TjimBitmapShape.Create(AOwner : TComponent);
  335. begin {Create}
  336.   inherited Create(AOwner);
  337.   FImages     := nil;
  338.   FImageIndex := 0;
  339.   FCaption    := nil;
  340. end;  {Create}
  341.  
  342.  
  343. procedure TjimBitmapShape.SetImages(Value : TImageList);
  344. begin {SetImages}
  345.   if Value <> FImages then begin
  346.     FImages := Value;
  347.  
  348.     if FImages <> nil then begin
  349.       // Set the size of the component to the image size
  350.       SetBounds(Left,Top,FImages.Width,FImages.Height);
  351.     end;
  352.   end;
  353. end;  {SetImages}
  354.  
  355.  
  356. procedure TjimBitmapShape.SetImageIndex(Value : Integer);
  357. begin {SetImageIndex}
  358.   if Value <> FImageIndex then begin
  359.     FImageIndex := Value;
  360.     Invalidate;
  361.   end;
  362. end;  {SetImageIndex}
  363.  
  364.  
  365. procedure TjimBitmapShape.SetCaption(Value : TjimTextShape);
  366. begin {SetCaption}
  367.   if (Value = nil) and Assigned(FCaption) then begin
  368.     FCaption.Free;
  369.   end else if (Value <> FCaption) then begin
  370.     FCaption := Value;
  371.     // Ensure the caption get aligned correctly
  372.     SetBounds(Left,Top,Width,Height);
  373.   end;
  374. end;  {SetCaption}
  375.  
  376.  
  377. procedure TjimBitmapShape.SetBounds(ALeft,ATop,AWidth,AHeight : Integer);
  378. begin {SetBounds}
  379.   inherited SetBounds(ALeft,ATop,AWidth,AHeight);
  380.  
  381.   // Set the position of the associated TjimTextShape control
  382.   if Assigned(FCaption) then begin
  383.     FCaption.SetBounds(ALeft,ATop + AHeight + 5,FCaption.Width,FCaption.Height);
  384.   end;
  385. end;  {SetBounds}
  386.  
  387.  
  388. procedure TjimBitmapShape.Paint;
  389. begin {Paint}
  390.   inherited Paint;
  391.  
  392.   if (not Assigned(Parent)) or
  393.      (not Assigned(FImages)) or
  394.      (FImageIndex < 0) or
  395.      (FImageIndex >= FImages.Count) then begin
  396.     // The component has not been placed on a form yet, or does not have an
  397.     // associated image
  398.     Exit;
  399.   end;
  400.  
  401.   FImages.DrawingStyle := dsTransparent;
  402.   FImages.Draw(Canvas,0,0,FImageIndex);
  403. end;  {Paint}
  404.  
  405.  
  406. procedure TjimBitmapShape.Notification(AComponent : TComponent;Operation : TOperation);
  407. begin {Notification}
  408.   inherited Notification(AComponent,Operation);
  409.  
  410.   if Operation = opRemove then begin
  411.     if AComponent = FImages then begin
  412.       FImages := nil;
  413.     end else if AComponent = FCaption then begin
  414.       FCaption := nil;
  415.     end;
  416.   end;
  417. end;  {Notification}
  418.  
  419.  
  420. // ----------------------------- TjimConnection ------------------------------
  421.  
  422. constructor TjimConnection.Create;
  423. begin {Create}
  424.   inherited Create;
  425.   FShape  := nil;
  426.   FSide   := csRight;
  427.   FOffset := 0;
  428. end;  {Create}
  429.  
  430.  
  431. procedure TjimConnection.Assign(Source : TPersistent);
  432. begin {Assign}
  433.   if Source is TjimConnection then begin
  434.     FShape  := TjimConnection(Source).FShape;
  435.     FSide   := TjimConnection(Source).FSide;
  436.     FOffset := TjimConnection(Source).FOffset;
  437.   end else begin
  438.     inherited Assign(Source);
  439.   end;
  440. end;  {Assign}
  441.  
  442.  
  443. function TjimConnection.ConnPoint(TerminatorRect : TRect): TPoint;
  444.   var
  445.     X,Y,W : Integer;
  446. begin {ConnPoint}
  447.   Result := Point(0,0);
  448.   X      := 0;
  449.   Y      := 0;
  450.   W      := TerminatorRect.Right - TerminatorRect.Left;
  451.  
  452.   if FShape = nil then begin
  453.     Exit;
  454.   end;
  455.  
  456.   case FSide of
  457.     csLeft   : begin
  458.       X := FShape.Left - W;
  459.       Y := FShape.Top + FOffset;
  460.     end;
  461.  
  462.     csRight  : begin
  463.       X := FShape.Left + FShape.Width - 1 + W;
  464.       Y := FShape.Top + FOffset;
  465.     end;
  466.  
  467.     csTop    : begin
  468.       X := FShape.Left + FOffset;
  469.       Y := FShape.Top - W;
  470.     end;
  471.  
  472.     csBottom : begin
  473.       X := FShape.Left + FOffset;
  474.       Y := FShape.Top  + FShape.Height - 1 + W;
  475.     end;
  476.   end;
  477.  
  478.   Result := Point(X,Y);
  479. end;  {ConnPoint}
  480.  
  481.  
  482. function TjimConnection.TermPoint(TerminatorRect : TRect): TPoint;
  483.   var
  484.     X,Y : Integer;
  485. begin {TermPoint}
  486.   Result := Point(0,0);
  487.   X      := 0;
  488.   Y      := 0;
  489.  
  490.   if FShape = nil then begin
  491.     Exit;
  492.   end;
  493.  
  494.   case FSide of
  495.     csLeft   : begin
  496.       X := FShape.Left;
  497.       Y := FShape.Top + FOffset;
  498.     end;
  499.  
  500.     csRight  : begin
  501.       X := FShape.Left + FShape.Width - 1;
  502.       Y := FShape.Top + FOffset;
  503.     end;
  504.  
  505.     csTop    : begin
  506.       X := FShape.Left + FOffset;
  507.       Y := FShape.Top;
  508.     end;
  509.  
  510.     csBottom : begin
  511.       X := FShape.Left + FOffset;
  512.       Y := FShape.Top  + FShape.Height - 1;
  513.     end;
  514.   end;
  515.  
  516.   Result := Point(X,Y);
  517. end;  {TermPoint}
  518.  
  519.  
  520. function TjimConnection.LeftMost(TerminatorRect : TRect): TPoint;
  521. begin {LeftMost}
  522.   Result := TermPoint(TerminatorRect);
  523.  
  524.   if FShape = nil then begin
  525.     Exit;
  526.   end;
  527.  
  528.   case FSide of
  529.     csLeft   : Result.X := FShape.Left - RectWidth(TerminatorRect);
  530.     csRight  : Result.X := FShape.Left + FShape.Width;
  531.     csTop,
  532.     csBottom : Result.X := FShape.Left + FOffset - (RectWidth(TerminatorRect) div 2);
  533.   end;
  534. end;  {LeftMost}
  535.  
  536.  
  537. function TjimConnection.RightMost(TerminatorRect : TRect): TPoint;
  538. begin {RightMost}
  539.   Result := TermPoint(TerminatorRect);
  540.  
  541.   if FShape = nil then begin
  542.     Exit;
  543.   end;
  544.  
  545.   case FSide of
  546.     csLeft   : Result.X := FShape.Left - 1;
  547.     csRight  : Result.X := FShape.Left + FShape.Width - 1 + RectWidth(TerminatorRect);
  548.     csTop,
  549.     csBottom : Result.X := FShape.Left + FOffset + (RectWidth(TerminatorRect) div 2);
  550.   end;
  551. end;  {RightMost}
  552.  
  553.  
  554. function TjimConnection.TopMost(TerminatorRect : TRect): TPoint;
  555. begin {TopMost}
  556.   Result := TermPoint(TerminatorRect);
  557.  
  558.   if FShape = nil then begin
  559.     Exit;
  560.   end;
  561.  
  562.   case FSide of
  563.     csLeft,
  564.     csRight  : Result.Y := FShape.Top + FOffset - (RectHeight(TerminatorRect) div 2);
  565.     csTop    : Result.Y := FShape.Top - RectHeight(TerminatorRect);
  566.     csBottom : Result.Y := FShape.Top + FShape.Height;
  567.   end;
  568. end;  {TopMost}
  569.  
  570.  
  571. function TjimConnection.BottomMost(TerminatorRect : TRect): TPoint;
  572. begin {BottomMost}
  573.   Result := TermPoint(TerminatorRect);
  574.  
  575.   if FShape = nil then begin
  576.     Exit;
  577.   end;
  578.  
  579.   case FSide of
  580.     csLeft,
  581.     csRight  : Result.Y := FShape.Top + FOffset + (RectHeight(TerminatorRect) div 2);
  582.     csTop    : Result.Y := FShape.Top - 1;
  583.     csBottom : Result.Y := FShape.Top + FShape.Height - 1 + RectHeight(TerminatorRect);
  584.   end;
  585. end;  {BottomMost}
  586.  
  587.  
  588. // ----------------------------- TjimConnector -------------------------------
  589.  
  590. constructor TjimConnector.Create(AOwner : TComponent);
  591. begin {Create}
  592.   inherited Create(AOwner);
  593.   FLineWidth     := 1;
  594.   FStartTermRect := Rect(0,0,0,0);
  595.   FEndTermRect   := Rect(0,0,0,0);
  596.   FStartConn     := TjimConnection.Create;
  597.   FEndConn       := TjimConnection.Create;
  598. end;  {Create}
  599.  
  600.  
  601. destructor TjimConnector.Destroy;
  602. begin {Destroy}
  603.   FStartConn.Free;
  604.   FEndConn.Free;
  605.   inherited Destroy;
  606. end;  {Destroy}
  607.  
  608.  
  609. procedure TjimConnector.Paint;
  610.   var
  611.     EndPt : TPoint;
  612. begin {Paint}
  613.   inherited Paint;
  614.  
  615.   if not Assigned(Parent) then begin
  616.     Exit;
  617.   end;
  618.  
  619.   if Assigned(FStartConn.Shape) and Assigned(FEndConn.Shape) then begin
  620.     // Draw the terminators (arrows etc)
  621.     DrawStartTerminator;
  622.     DrawEndTerminator;
  623.  
  624.     with Canvas do begin
  625.       // Draw the connecting line
  626.       Brush.Style := bsClear;
  627.       Pen.Width   := FLineWidth;
  628.       Pen.Color   := clBlack;
  629.       // Convert from Parent coordinates to control coordinates
  630.       PenPos      := Convert(FStartConn.ConnPoint(FStartTermRect));
  631.       EndPt       := Convert(FEndConn.ConnPoint(FEndTermRect));
  632.       LineTo(EndPt.X,EndPt.Y);
  633.     end;
  634.   end;
  635. end;  {Paint}
  636.  
  637.  
  638. procedure TjimConnector.Notification(AComponent : TComponent;Operation : TOperation);
  639. begin {Notification}
  640.   inherited Notification(AComponent,Operation);
  641.  
  642.   if Operation = opRemove then begin
  643.     if AComponent = FStartConn.FShape then begin
  644.       FStartConn.FShape := nil;
  645.     end;
  646.  
  647.     if AComponent = FEndConn.FShape then begin
  648.       FEndConn.FShape := nil;
  649.     end;
  650.   end;
  651. end;  {Notification}
  652.  
  653.  
  654. procedure TjimConnector.DrawStartTerminator;
  655. begin {DrawStartTerminator}
  656. end;  {DrawStartTerminator}
  657.  
  658.  
  659. procedure TjimConnector.DrawEndTerminator;
  660. begin {DrawEndTerminator}
  661. end;  {DrawEndTerminator}
  662.  
  663.  
  664. procedure TjimConnector.SetBounds(ALeft,ATop,AWidth,AHeight : Integer);
  665. begin {SetBounds}
  666.   // Ensure the control is at least as big as the line width
  667.   NoLessThan(AHeight,FLineWidth);
  668.   NoLessThan(AWidth,FLineWidth);
  669.   // Ensure the control is at least as big as the start terminator rectangle
  670.   NoLessThan(AHeight,RectHeight(FStartTermRect));
  671.   NoLessThan(AWidth,RectWidth(FStartTermRect));
  672.   // Ensure the control is at least as big as the end terminator rectangle
  673.   NoLessThan(AHeight,RectHeight(FEndTermRect));
  674.   NoLessThan(AWidth,RectWidth(FEndTermRect));
  675.  
  676.   inherited SetBounds(ALeft,ATop,AWidth,AHeight);
  677. end;  {SetBounds}
  678.  
  679.  
  680. procedure TjimConnector.SetLineWidth(Value : Integer);
  681. begin {SetLineWidth}
  682.   // Ensure that can always see the line!
  683.   if Value >= 1 then begin
  684.     FLineWidth := Value;
  685.   end;
  686. end;  {SetLineWidth}
  687.  
  688.  
  689. function TjimConnector.GetConn(Index : Integer) : TjimConnection;
  690. begin {GetConn}
  691.   Result := nil;
  692.  
  693.   case Index of
  694.     1 : Result := FStartConn;
  695.     2 : Result := FEndConn;
  696.   end;
  697. end;  {GetConn}
  698.  
  699.  
  700. procedure TjimConnector.SetConn(Index : Integer;Value : TjimConnection);
  701. begin {SetConn}
  702.   case Index of
  703.     1 : FStartConn.Assign(Value);
  704.     2 : FEndConn.Assign(Value);
  705.   end;
  706.  
  707.   SetBoundingRect;
  708. end;  {SetConn}
  709.  
  710.  
  711. procedure TjimConnector.SetBoundingRect;
  712.   var
  713.     L,T,W,H : Integer;
  714. begin {SetBoundingRect}
  715.   if (FStartConn.Shape = nil) or (FEndConn.Shape = nil) then begin
  716.     Exit;
  717.   end;
  718.  
  719.   L := Min([FStartConn.LeftMost(FStartTermRect).X,
  720.             FEndConn.LeftMost(FEndTermRect).X]);
  721.   T := Min([FStartConn.TopMost(FStartTermRect).Y,
  722.             FEndConn.TopMost(FEndTermRect).Y]);
  723.   W := Max([FStartConn.RightMost(FStartTermRect).X,
  724.             FEndConn.RightMost(FEndTermRect).X]) -
  725.        Min([FStartConn.LeftMost(FStartTermRect).X,
  726.             FEndConn.LeftMost(FEndTermRect).X]) + 1;
  727.   H := Max([FStartConn.BottomMost(FStartTermRect).Y,
  728.             FEndConn.BottomMost(FEndTermRect).Y]) -
  729.        Min([FStartConn.TopMost(FStartTermRect).Y,
  730.             FEndConn.TopMost(FEndTermRect).Y]) + 1;
  731.   SetBounds(L,T,W,H);
  732.   SetZOrder(False);  // Move to bottom
  733. end;  {SetBoundingRect}
  734.  
  735.  
  736. procedure TjimConnector.SetConnections(TheStartConn,TheEndConn : TjimConnection);
  737. begin {SetConnections}
  738.   StartConn := TheStartConn;
  739.   EndConn   := TheEndConn;
  740.   SetZOrder(False);  // Move to bottom
  741. end;  {SetConnections}
  742.  
  743.  
  744. function TjimConnector.GetTermRect(Index : Integer) : TRect;
  745. begin {GetTermRect}
  746.   case Index of
  747.     1 : Result := FStartTermRect;
  748.     2 : Result := FEndTermRect;
  749.   end;
  750. end;  {GetTermRect}
  751.  
  752.  
  753. procedure TjimConnector.SetTermRect(Index : Integer;Value : TRect);
  754. begin {SetTermRect}
  755.   if (Value.Right - Value.Left >= 0) and (Value.Bottom - Value.Top >= 0) then begin
  756.     case Index of
  757.       1 : FStartTermRect := Value;
  758.       2 : FEndTermRect   := Value;
  759.     end;
  760.   end;
  761. end;  {SetTermRect}
  762.  
  763.  
  764. function TjimConnector.Convert(APoint : TPoint) : TPoint;
  765. begin {Convert}
  766.   Result := ScreenToClient(Parent.ClientToScreen(APoint));
  767. end;  {Convert}
  768.  
  769.  
  770. function TjimConnector.IsConnected(ConnectedShape : TjimCustomShape) : Boolean;
  771. begin {IsConnected}
  772.   Result := (FStartConn.Shape = ConnectedShape) or
  773.             (FEndConn.Shape = ConnectedShape);
  774. end;  {IsConnected}
  775.  
  776.  
  777. // ------------------------- TjimSingleHeadArrow ---------------------------
  778.  
  779. constructor TjimSingleHeadArrow.Create(AOwner : TComponent);
  780. begin {Create}
  781.   inherited Create(AOwner);
  782.   EndTermRect := Rect(0,0,25,10);
  783. end;  {Create}
  784.  
  785.  
  786. procedure TjimSingleHeadArrow.DrawArrowHead(ConnPt,TermPt : TPoint);
  787.   var
  788.     PointPt,Corner1Pt,Corner2Pt : TPoint;
  789. begin {DrawArrowHead}
  790.   with Canvas do begin
  791.     Brush.Style := bsSolid;
  792.     Brush.Color := clBlack;
  793.  
  794.     // Draw a line connecting the Conn and Term points
  795.     PenPos    := ConnPt;
  796.     LineTo(TermPt.X,TermPt.Y);
  797.     // Set the basic points (to be modified depending on arrow head direction
  798.     PointPt   := TermPt;
  799.     Corner1Pt := ConnPt;
  800.     Corner2Pt := ConnPt;
  801.  
  802.     if ConnPt.X < TermPt.X then begin
  803.       // Draw a right pointing arrow head
  804.       Inc(Corner1Pt.X,10);
  805.       Inc(Corner2Pt.X,10);
  806.       Dec(Corner1Pt.Y,RectHeight(EndTermRect) div 2);
  807.       Inc(Corner2Pt.Y,RectHeight(EndTermRect) div 2);
  808.     end else if ConnPt.X > TermPt.X then begin
  809.       // Draw a left pointing arrow head
  810.       Dec(Corner1Pt.X,10);
  811.       Dec(Corner2Pt.X,10);
  812.       Dec(Corner1Pt.Y,RectHeight(EndTermRect) div 2);
  813.       Inc(Corner2Pt.Y,RectHeight(EndTermRect) div 2);
  814.     end else if ConnPt.Y < TermPt.Y then begin
  815.       // Draw a down pointing arrow head
  816.       Inc(Corner1Pt.Y,10);
  817.       Inc(Corner2Pt.Y,10);
  818.       Dec(Corner1Pt.X,RectHeight(EndTermRect) div 2);
  819.       Inc(Corner2Pt.X,RectHeight(EndTermRect) div 2);
  820.     end else begin
  821.       // Draw a up pointing arrow head
  822.       Dec(Corner1Pt.Y,10);
  823.       Dec(Corner2Pt.Y,10);
  824.       Dec(Corner1Pt.X,RectHeight(EndTermRect) div 2);
  825.       Inc(Corner2Pt.X,RectHeight(EndTermRect) div 2);
  826.     end;
  827.  
  828.     Polygon([PointPt,Corner1Pt,Corner2Pt]);
  829.   end;
  830. end;  {DrawArrowHead}
  831.  
  832.  
  833. procedure TjimSingleHeadArrow.DrawEndTerminator;
  834.   var
  835.     ConnPt,TermPt : TPoint;
  836. begin {DrawEndTerminator}
  837.   inherited DrawEndTerminator;
  838.  
  839.   if Assigned(FEndConn.Shape) then begin
  840.     ConnPt := Convert(FEndConn.ConnPoint(EndTermRect));
  841.     TermPt := Convert(FEndConn.TermPoint(EndTermRect));;
  842.     DrawArrowHead(ConnPt,TermPt);
  843.   end;
  844. end;  {DrawEndTerminator}
  845.  
  846.  
  847. end.
  848.